iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0
Python

用 Python 打造你的 Discord BOT系列 第 22

[Day 22] bot 指令管理 (二):Extension

  • 分享至 

  • xImage
  •  

今天來認識 Cog 的好夥伴 ── Extension。

進度

Extension 是與 Cog 不同的另一種指令管理做法,同樣可以做到拆分檔案的效果。而且,更厲害的是,Extension 還有 hot-reloading 的功能,在實際應用時更廣泛。

雖然通常 Extension 會與 Cog 搭配使用,但其實就算沒有 Cog,Extension 也可以獨立運作。

Extension

Extension 本質就是 .py 檔,但一定要有一個 coroutine 的 setup 函數作為進入點 (entry point),並且這個函數只能有一個參數 bot。

Extension 架構

在看範例之前,先來看一下 Extension 架構

from discord.ext import commands

# 在這邊加 command、listener 

async def setup(bot: commands.Bot):
    await bot.add_command(hello)  # hello 為示範的 command 名稱

或是結合 Cog (這是比較常見的用法)

from discord.ext import commands

class MyCog(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot

    # 在這邊加 command、listener 

async def setup(bot: commands.Bot):
    await bot.add_cog(MyCog(bot))

如同前面所述,最關鍵的就是 setup 函數,至於裡面到底要直接用 add_command 還是用 add_cog 都可以。

第一個 Extension

這個範例的架構有點像昨天的 Cog 的範例。

# utils/hello.py

@commands.command()
async def hello(ctx: commands.Context):
    await ctx.send(f'Hello {ctx.author.display_name}.')

async def setup(bot: commands.Bot):
    bot.add_command(hello)
# main.py

import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='', intents=intents)

@bot.command()
async def load(ctx: commands.Context):
    await bot.load_extension("utils.hello")
    await ctx.send("hello extension loaded", ephemeral=True)

@bot.command()
async def reload(ctx: commands.Context):
    await bot.reload_extension("utils.hello")
    await ctx.send("hello extension reloaded", ephemeral=True)

@bot.command()
async def unload(ctx: commands.Context):
    await bot.unload_extension("utils.hello")
    await ctx.send("hello extension unloaded", ephemeral=True)

@bot.event
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
    await ctx.send(f"發生錯誤了: {error}")

bot.run('token')

執行後,如果直接輸入 hello,會觸發 CommandNotFound 的錯誤。

但是,如果輸入 load,就可以使用 hello 了。

此時,如果去修改 utils/hello.py 內的訊息,接著輸入 reload (不必重啟主程式),可以發現訊息發生變化了!這就是所謂的「hot-reloading」!

@commands.command()
async def hello(ctx):
    await ctx.send(f'Hello {ctx.author.display_name}. (version 2)')

最後,如果再輸入 unload,就又回到無法使用 hello 的狀態。

發生了什麼事?

有了昨天 Cog 的經驗,這次應該很好理解。重點有三個:

  1. load 觸發的 load_extension
  2. reload 觸發的 reload_extension
  3. unload 觸發的 unload_extension

相較於 Cog,Extension 這三個函數的參數格式比較統一,都是 Extension 的名稱,格式為字串 (str)。

Extension 名稱基本上就是 .py 檔的檔名。名稱的規則與 Python 的套件一樣:

  • 不用加 .py
  • 如果是在某個資料夾內,就用 . 隔開。(例如:在 utils 資料夾內的 hello.py,就要用 utils.hello)

如果不喜歡這麼「絕對路徑」的寫法,也可以換個比較「相對路徑」的寫法:

@bot.command()
async def load(ctx: commands.Context):
    await bot.load_extension(".hello", package="utils")
    await ctx.send("hello extension loaded", ephemeral=True)

Extension 的更多應用

既然 Extension 使用的是檔案名稱,那麼其實就可以利用 os.listdir 去取得所有檔案名稱並一次性全部載入,不需要去管到底檔案名稱是什麼。

# ...

@bot.command()
async def load(ctx: commands.Context):
    for filename in os.listdir("./ext"):
        if filename.endswith(".py"):
            await bot.load_extension(f"ext.{filename[:-3]}")
            await ctx.send(f"{filename[:-3]} loaded", ephemeral=True)

# ...

小結

今天介紹了 Extension 的用法,包含建立、載入、更新、移除。bot 指令管理的部分就在此告一個段落了~


上一篇
[Day 21] bot 指令管理 (一):Cog
下一篇
[Day 23] 部屬 (一):部屬觀念 與 Replit 美好的過去
系列文
用 Python 打造你的 Discord BOT31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言